package cn.cj.erp.filter;

import cn.cj.erp.common.redis.RedisKeyEnum;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.cj.erp.common.http.Result;
import cn.cj.erp.common.redis.RedisCache;
import cn.cj.erp.common.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * 校验过滤器，防重，防篡改
 * @author chenjian
 */
@Slf4j
@WebFilter
@ServletComponentScan
@Component
public class SignAuthFilter implements Filter {
    /**
     * 请求ID 防止重放
     */
    private static final String REQUEST_ID = "requestId";
    /**
     * 请求时间 避免缓存时间过后重放
     */
    private static final String TIMESTAMP = "timestamp";
    /**
     * requestId 防重时间，2秒
     */
    private static final long EXPIRE_REQUEST_ID = 2L;

    @Resource
    private RedisCache redisCache;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String requestId = request.getHeader(REQUEST_ID);
        String timestamp = request.getHeader(TIMESTAMP);
        if (!validateHeader(request, requestId, timestamp)) {
            // 存在空，则拒绝服务
            ServletUtils.renderResult(Result.error(HttpStatus.FORBIDDEN.value(), HttpStatus.FORBIDDEN.getReasonPhrase()));
            return;
        }
        // 放行
        filterChain.doFilter(servletRequest, servletResponse);
    }

    private boolean validateHeader(HttpServletRequest request, String requestId, String timestamp) {
        // 判断Nonce和Timestamp是否为空
        if (StrUtil.isEmpty(requestId) || StrUtil.isEmpty(timestamp)) {
            log.error("【{}】【{}】请求不合法，存在空参数，被网关拒绝",
                    ServletUtil.getClientIP(request),
                    request.getRequestURI());
            // 存在空，则拒绝服务
            return false;
        }
        // 验证 requestId 是否已经使用过
        String value = redisCache.get(RedisKeyEnum.REQUEST_ID.getKey(requestId));
        if (StrUtil.isNotBlank(value)) {
            log.error("【{}】【{}】请求不合法，requestId【{}】已被使用，被网关拒绝",
                    ServletUtil.getClientIP(request),
                    request.getRequestURI(),
                    requestId);
            // 被使用过则拒绝服务
            return false;
        } else {
            redisCache.set(requestId, requestId, EXPIRE_REQUEST_ID);
        }
        return true;
    }
}
